package cn.lzy.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * @Description 五种通知
 * @Author lzy
 * @Date 2023/12/18 9:29
 * @Version
 **/
// Aspect = Pointcut + Advice
@Aspect
@Component
// @EnableAspectJAutoProxy：这个相当于 <aop:aspectj-autoproxy/>，这个注解是用来识别项目中的 Aspect 的
@EnableAspectJAutoProxy
public class LogAspect {


    @Pointcut("execution(* cn.lzy.demo.CalculatorImpl.*(..))")
    public void pc() {

    }

    /**
     * 前置通知，在被拦截下来的方法执行之前触发
     * @param joinPoint
     */
    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法开始执行。");
    }


    /**
     * 后置通知，在目标方法执行之后执行
     * @param joinPoint
     */
    @After("pc()")
    public void after(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法执行结束");
    }


    /**
     * 环绕通知
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("pc()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        // 这行代码表示执行目标方法
        Object result = pjp.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println(pjp.getSignature().getName() + "方法执行耗时：" + (endTime - startTime) + "毫秒");
        return result;
    }


    /**
     * 异常通知，目标方法执行出错的时候触发
     * 异常通知要求方法所抛出的异常类型必须和异常通知中的参数的异常类型相匹配（即相等或是异常类型的子类），才会进入到这个方法中
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "pc()", throwing = "e")
    public void exception(JoinPoint joinPoint, Exception e) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法执行抛出" + e.getMessage() + "异常");
    }


    /**
     * 这个是返回通知，被拦截下来的目标方法执行完毕之后，会触发这个方法
     *
     * @param joinPoint
     * @param result    目标方法的返回值，如果返回值为 void，则返回 null
     *                  (目标方法的返回值必须和这里的返回参数相匹配，该方法才会被触发)
     */
    @AfterReturning(pointcut = "execution(* cn.lzy.demo.CalculatorImpl.minus(..))", returning = "result")
    public void returnAdvice(JoinPoint joinPoint, Object result) {
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法返回值：" + result);
    }
}
